package com.facebook.drawee.drawable;

import android.graphics.Canvas;
import android.graphics.Matrix;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;

import com.facebook.common.internal.Preconditions;
import com.facebook.common.internal.VisibleForTesting;

public final class ScaleTypeDrawable extends ForwardingDrawable {
    @VisibleForTesting
    public ScalingUtils.ScaleType mScaleType;
    @VisibleForTesting
    private int mUnderlyingWidth = 0;
    @VisibleForTesting
    private int mUnderlyingHeight = 0;
    @VisibleForTesting
    private Matrix mDrawMatrix;
    private Matrix mTempMatrix = new Matrix();

    public ScaleTypeDrawable(Drawable drawable, ScalingUtils.ScaleType scaleType) {
        super(Preconditions.checkNotNull(drawable));
        this.mScaleType = scaleType;
    }

    public final void getTransform(Matrix matrix) {
        this.getParentTransform(matrix);
        this.configureBoundsIfUnderlyingChanged();
        if (this.mDrawMatrix != null) {
            matrix.preConcat(this.mDrawMatrix);
        }
    }

    @VisibleForTesting
    public final void configureBounds() {
        Drawable underlyingDrawable = this.getCurrent();
        Rect bounds = this.getBounds();
        int viewWidth = bounds.width();
        int viewHeight = bounds.height();
        int underlyingWidth = underlyingDrawable.getIntrinsicWidth();
        this.mUnderlyingWidth = underlyingWidth;
        int underlyingHeight = underlyingDrawable.getIntrinsicHeight();
        this.mUnderlyingHeight = underlyingHeight;
        if (underlyingWidth <= 0 || underlyingHeight <= 0) {
            underlyingDrawable.setBounds(bounds);
            this.mDrawMatrix = null;
        } else {
            if (underlyingWidth == viewWidth && underlyingHeight == viewHeight) {
                underlyingDrawable.setBounds(bounds);
                this.mDrawMatrix = null;
                return;
            }
            if (this.mScaleType == ScalingUtils.ScaleType.FIT_XY) {
                underlyingDrawable.setBounds(bounds);
                this.mDrawMatrix = null;
                return;
            }
            underlyingDrawable.setBounds(0, 0, underlyingWidth, underlyingHeight);
            ScalingUtils.ScaleType scaleType = this.mScaleType;
            int childWidth = bounds.width();
            int childHeight = bounds.height();
            float scaleX = (float) childWidth / (float) underlyingWidth;
            float scaleY = (float) childHeight / (float) underlyingHeight;
            switch (scaleType) {
                case FIT_XY:
                    getTransformFitXY(mTempMatrix, scaleX, scaleY, bounds);
                    break;
                case FIT_START:
                    getTransformFitStart(mTempMatrix, scaleX, scaleY, bounds);
                    break;
                case FIT_CENTER:
                    getTransformFitCenter(mTempMatrix, scaleX, scaleY, underlyingWidth, underlyingHeight, bounds);
                    break;
                case FIT_END:
                    getTransformFitEnd(mTempMatrix, scaleX, scaleY, underlyingWidth, underlyingHeight, bounds);
                    break;
                case CENTER:
                    getTransformCenter(mTempMatrix, underlyingWidth, underlyingHeight, bounds);
                    break;
                case CENTER_INSIDE:
                    getTransformInside(mTempMatrix, scaleX, scaleY, underlyingWidth, underlyingHeight, bounds);
                    break;
                case CENTER_CROP:
                    getTransformCenterCrop(mTempMatrix, scaleX, scaleY, underlyingWidth, underlyingHeight, bounds);
                    break;
                case FOCUS_CROP:
                    getTransformFocusCrop(mTempMatrix, scaleX, scaleY, underlyingWidth, underlyingHeight, bounds);
                    break;
                default:
                    throw new UnsupportedOperationException("Unsupported scale type: " + scaleType);
            }
            this.mDrawMatrix = this.mTempMatrix;
        }
    }

    private void getTransformFocusCrop(Matrix matrix, float scaleX, float scaleY, int underlyingWidth, int underlyingHeight, Rect bounds) {
        float scale;
        float dx;
        float dy;
        if (scaleY > scaleX) {
            scale = scaleY;
            dx = Math.max(Math.min((float) bounds.width() * 0.5f - (float) underlyingWidth * scale * 0.5f, 0f),
                    (float) bounds.width() - (float) underlyingWidth * scale) + (float) bounds.left;
            dy = ((float) bounds.top);
        } else {
            scale = scaleX;
            dx = (float) bounds.left;
            dy = (float) bounds.top + Math.max(Math.min((float) bounds.height() * 0.5f - (float) underlyingHeight * scale * 0.5f, 0f),
                    (float) bounds.height() - (float) underlyingHeight * scale);
        }
        matrix.setScale(scale, scale);
        matrix.postTranslate((float) (int) (dx + 0.5f), ((float) (int) (dy + 0.5f)));
    }

    private void getTransformCenterCrop(Matrix matrix, float scaleX, float scaleY, int underlyingWidth, int underlyingHeight, Rect bounds) {
        float dx;
        float dy;
        float scale;
        if (scaleY > scaleX) {
            scale = scaleY;
            dx = (float) bounds.left + ((float) bounds.width() - (float) underlyingWidth * scale) * 0.5f;
            dy = (float) bounds.top;
        } else {
            scale = scaleX;
            dx = ((float) bounds.left);
            dy = (((float) bounds.top)) + ((float) bounds.height() - (float) underlyingHeight * scale) * 0.5f;
        }
        matrix.setScale(scale, scale);
        matrix.postTranslate((float) (int) (dx + 0.5f), (float) (int) (dy + 0.5f));
    }

    private void getTransformInside(Matrix matrix, float scaleX, float scaleY, int underlyingWidth, int underlyingHeight, Rect bounds) {
        float scale = Math.min(Math.min(scaleX, scaleY), 1f);
        float dx = (float) bounds.left + ((float) bounds.width() - (float) underlyingWidth * scale) * 0.5f;
        float dy = (float) bounds.top + ((float) bounds.height() - (float) underlyingHeight * scale) * 0.5f;
        matrix.setScale(scale, scale);
        matrix.postTranslate((float) (int) (dx + 0.5f), (float) (int) (dy + 0.5f));
    }

    private void getTransformCenter(Matrix matrix, int childWidth, int childHeight, Rect bounds) {
        matrix.setTranslate((float) (int) ((float) bounds.left + (float) (bounds.width() - childWidth) * 0.5f + 0.5f),
                (float) (int) ((float) bounds.top + (float) (bounds.height() - childHeight) * 0.5f + 0.5f));
    }

    private void getTransformFitEnd(Matrix matrix, float scaleX, float scaleY, int childWidth, int childHeight, Rect bounds) {
        float scale = Math.min(scaleX, scaleY);
        float dx = (float) bounds.left + ((float) bounds.width() - (float) scale * scaleY);
        float dy = (float) bounds.top + ((float) bounds.height() - (float) scale * scaleY);
        matrix.setScale(scale, scale);
        matrix.postTranslate((float) (int) (dx + 0.5f), (float) (int) (dy + 0.5f));
    }

    private void getTransformFitCenter(Matrix matrix, float scaleX, float scaleY, int childWidth, int childHeight, Rect bounds) {
        float scale = Math.min(scaleX, scaleY);
        float dx = (float) bounds.left + ((float) bounds.width() - (float) childWidth * scale) * 0.5f;
        float dy = (float) bounds.top + ((float) bounds.height() - (float) childHeight * scale) * 0.5f;
        matrix.setScale(scale, scale);
        matrix.postTranslate(((float) (((int) (dx + 0.5f)))), ((float) (((int) (dy + 0.5f)))));
    }

    private void getTransformFitXY(Matrix matrix, float scaleX, float scaleY, Rect parentRect) {
        float dx = (float) parentRect.left;
        float dy = (float) parentRect.top;
        matrix.setScale(scaleX, scaleY);
        matrix.postTranslate((float) (int) (dx + 0.5f), (float) (int) (dy + 0.5f));
    }

    private void getTransformFitStart(Matrix matrix, float scaleX, float scaleY, Rect parentRect) {
        float scale = Math.min(scaleX, scaleY);
        float dx = (float) parentRect.left;
        float dy = (float) parentRect.top;
        matrix.setScale(scale, scale);
        matrix.postTranslate((float) (int) (dx + 0.5f), (float) (int) (dy + 0.5f));
    }

    private void configureBoundsIfUnderlyingChanged() {
        if (this.mUnderlyingWidth != this.getCurrent().getIntrinsicWidth()
                || this.mUnderlyingHeight != this.getCurrent().getIntrinsicHeight()) {
            this.configureBounds();
        }
    }

    public final void draw(Canvas canvas) {
        this.configureBoundsIfUnderlyingChanged();
        if (this.mDrawMatrix != null) {
            int v0 = canvas.save();
            canvas.clipRect(this.getBounds());
            canvas.concat(this.mDrawMatrix);
            super.draw(canvas);
            canvas.restoreToCount(v0);
        } else {
            super.draw(canvas);
        }
    }

    protected final void onBoundsChange(Rect rect) {
        this.configureBounds();
    }
}

